﻿using System;
using System.Web;
using MvcContrib.Pagination;
using System.Text;
using System.Collections.Specialized;

namespace Framework.Util.Helpers
{
	public class ExtPager
	{
		private readonly IPagination _pagination;
		private readonly HttpRequestBase _request;

		private string _paginationFormat = "Showing {0} - {1} of {2} ";
		private string _paginationSingleFormat = "Showing {0} of {1} ";
		private string _paginationFirst = "first";
		private string _paginationPrev = "prev";
		private string _paginationNext = "next";
		private string _paginationLast = "last";
		private string _paginationLink = "{first} {previous} {links} {next} {last}";
		private int _numOfLeaf = 3;
		private string _pageQueryName = "page";
		private Func<int, string> _urlBuilder;

		/// <summary>
		/// Creates a new instance of the Pager class.
		/// </summary>
		/// <param name="pagination">The IPagination datasource</param>
		/// <param name="request">The current HTTP Request</param>
		public ExtPager(IPagination pagination, HttpRequestBase request)
		{
			_pagination = pagination;
			_request = request;

			_urlBuilder = CreateDefaultUrl;
		}

		/// <summary>
		/// Specifies the query string parameter to use when generating pager links. The default is 'page'
		/// </summary>
		public ExtPager QueryParam(string queryStringParam)
		{
			_pageQueryName = queryStringParam;
			return this;
		}

		/// <summary>
		/// Specifies the format to use when rendering a pagination containing a single page. 
		/// The default is 'Showing {0} of {1}' (eg 'Showing 1 of 3')
		/// </summary>
		public ExtPager SingleFormat(string format)
		{
			_paginationSingleFormat = format;
			return this;
		}

		/// <summary>
		/// Specifies the format to use when rendering a pagination containing multiple pages. 
		/// The default is 'Showing {0} - {1} of {2}' (eg 'Showing 1 to 3 of 6')
		/// </summary>
		public ExtPager Format(string format)
		{
			_paginationFormat = format;
			return this;
		}

		/// <summary>
		/// Text for the 'first' link.
		/// </summary>
		public ExtPager First(string first)
		{
			_paginationFirst = first;
			return this;
		}

		/// <summary>
		/// Text for the 'prev' link
		/// </summary>
		public ExtPager Previous(string previous)
		{
			_paginationPrev = previous;
			return this;
		}

		/// <summary>
		/// Text for the 'next' link
		/// </summary>
		public ExtPager Next(string next)
		{
			_paginationNext = next;
			return this;
		}

		/// <summary>
		/// Text for the 'last' link
		/// </summary>
		public ExtPager Last(string last)
		{
			_paginationLast = last;
			return this;
		}

		/// <summary>
		/// Input the links format
		/// </summary>
		/// <param name="links">Format</param>
		/// <returns></returns>
		public ExtPager LinksFormat(string links)
		{
			_paginationLink = links;
			return this;
		}

		/// <summary>
		/// Set the number of pages to display
		/// </summary>
		/// <param name="number"></param>
		/// <returns></returns>
		public ExtPager NumOfLeaf(int number)
		{
			_numOfLeaf = number;
			return this;
		}

		/// <summary>
		/// Uses a lambda expression to generate the URL for the page links.
		/// </summary>
		/// <param name="urlBuilder">Lambda expression for generating the URL used in the page links</param>
		public ExtPager Link(Func<int, string> urlBuilder)
		{
			_urlBuilder = urlBuilder;
			return this;
		}

		public override string ToString()
		{
			if(_pagination.TotalItems == 0)
				return null;

			var builder = new StringBuilder();
			builder.Append("<div class='pagination'> ");
			RenderLeftSideOfPager(builder);

			if(_pagination.TotalPages > 1)
				RenderRightSideOfPager(builder);

			builder.Append(@"</div>");

			return builder.ToString();
		}

		protected virtual void RenderLeftSideOfPager(StringBuilder builder)
		{
			//builder.Append("<span class='paginationLeft'>");

			//Special case handling where the page only contains 1 item (ie it's a details-view rather than a grid)
			if(_pagination.PageSize == 1)
			{
				RenderNumberOfItemsWhenThereIsOnlyOneItemPerPage(builder);
			}
			else
			{
				RenderNumberOfItemsWhenThereAreMultipleItemsPerPage(builder);
			}
			//builder.Append("</span>");
		}

		protected virtual void RenderRightSideOfPager(StringBuilder builder)
		{
			var pagestring = _paginationLink
				.Replace("{first}", "{0}")
				.Replace("{previous}", "{1}")
				.Replace("{links}", "{2}")
				.Replace("{next}", "{3}")
				.Replace("{last}", "{4}");

			var first = _pagination.PageNumber >= 2 ?
				CreatePageLink(1, _paginationFirst, "page_first") :
				"";
			var previous = _pagination.HasPreviousPage ?
				CreatePageLink(_pagination.PageNumber - 1, _paginationPrev, "page_prev") :
				"";
			var next = _pagination.HasNextPage ?
				CreatePageLink(_pagination.PageNumber + 1, _paginationNext, "page_next") :
				"";
			var last = _pagination.PageNumber + 1 <= _pagination.TotalPages ?
				CreatePageLink(_pagination.TotalPages, _paginationLast, "page_last") :
				"";

			var half = _numOfLeaf / 2;
			var from = _pagination.PageNumber - half;
			if (from < 1) from = 1;
			var to = from + _numOfLeaf - 1;
			if (to > _pagination.TotalPages)
			{
				to = _pagination.TotalPages;
				from = to - _numOfLeaf + 1;
				if (from < 1) from = 1;
			}
			var links = new StringBuilder();
			for (int i = from; i <= to; i++)
				links.Append(CreatePageLink(i, i.ToString(), "page_leaf"));

			builder.Append("<span class='paginationRight'>");
			builder.Append(string.Format(pagestring, first, previous, links.ToString(), next, last));
			builder.Append("</span>");
		}


		protected virtual void RenderNumberOfItemsWhenThereIsOnlyOneItemPerPage(StringBuilder builder) 
		{
			builder.AppendFormat(_paginationSingleFormat, _pagination.FirstItem, _pagination.TotalItems);
		}

		protected virtual void RenderNumberOfItemsWhenThereAreMultipleItemsPerPage(StringBuilder builder) 
		{
			builder.AppendFormat(_paginationFormat, _pagination.FirstItem, _pagination.LastItem, _pagination.TotalItems);
		}

		private string CreatePageLink(int pageNumber, string text, string strClass)
		{
			const string link = "<a href=\"{0}\" class=\"page_{1} {3}\">{2}</a>";
			const string span = "<span class=\"page_active {1}\">{0}</span>";
			return pageNumber == _pagination.PageNumber ?
				string.Format(span, text, strClass) :
				string.Format(link, _urlBuilder(pageNumber), pageNumber, text, strClass);
		}

		private string CreateDefaultUrl(int pageNumber)
		{
			var queryString = CreateQueryString(_request.QueryString);
			var filePath = _request.FilePath;
			var url = string.Format("{0}?{1}={2}{3}", filePath, _pageQueryName, pageNumber, queryString);
			return url;
		}

		private string CreateQueryString(NameValueCollection values)
		{
			var builder = new StringBuilder();

			foreach(string key in values.Keys)
			{
				if(key == _pageQueryName)
					//Don't re-add any existing 'page' variable to the querystring - this will be handled in CreatePageLink.
				{
					continue;
				}

				foreach(var value in values.GetValues(key))
				{
					builder.AppendFormat("&amp;{0}={1}", key, HttpUtility.HtmlEncode(value));
				}
			}

			return builder.ToString();
		}
	}
}